package com.cman777.springc.sample.config.idempotence;

import com.cman777.springc.idempotence.ResultWrapper;
import com.cman777.springc.idempotence.storage.Storage;
import com.cman777.springc.redis.annotation.RedisLock;
import com.cman777.springc.sample.bean.po.Idempotence;
import com.cman777.springc.sample.service.IdempotenceService;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.util.function.Supplier;

/**
 * @author chenzhicong
 * @time 2020/9/15 14:16
 */
@Component
@Log4j2
public class MybatisPlusStorageAdapter implements Storage {
    @Autowired
    private IdempotenceService idempotenceService;


    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @RedisLock(entity = Idempotence.class, salt = "#key")
    public boolean setIfAbsent(String key, Supplier<ResultWrapper> supplier) {
        if (this.exists(key)) {
            return false;
        } else {
            return this.setIfAbsent(key, supplier.get());
        }
    }

    @Override
    @RedisLock(entity = Idempotence.class, salt = "#key")
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public boolean setIfAbsent(String key, ResultWrapper value) {
        Idempotence idempotence = idempotenceService.selectByIdempotentceId(key);
        if (idempotence != null) {
            return false;
        } else {
            String valueStr = null;
            try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                 ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream)) {
                out.writeObject(value);
                byte[] exceptionBytes = byteArrayOutputStream.toByteArray();
                valueStr = Base64.encodeBase64String(exceptionBytes);
            } catch (Exception ex) {
                log.error("序列化错误", ex);
                return false;
            }
            Idempotence idempotenceNew = new Idempotence();
            idempotenceNew.setIdempotenceId(key);
            idempotenceNew.setValue(valueStr);
            idempotenceService.save(idempotenceNew);
            return true;
        }
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public boolean delete(String idempotenceId) {
        idempotenceService.deleteByIdempotentceId(idempotenceId);
        return true;
    }

    @Override
    public <T extends Serializable> ResultWrapper<T> getResult(String idempotenceId) {
        Idempotence idempotence = idempotenceService.selectByIdempotentceId(idempotenceId);
        ResultWrapper<T> resultWrapper = null;
        String value = idempotence.getValue();
        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Base64.decodeBase64(value)))) {
            resultWrapper = (ResultWrapper<T>) ois.readObject();
        } catch (Exception e) {
            log.error("反序列化错误", e);
        }
        return resultWrapper;
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public boolean exists(String key) {
        Idempotence idempotence = idempotenceService.selectByIdempotentceId(key);
        if (idempotence != null) {
            return true;
        } else {
            return false;
        }
    }
}
